%%html
<style>
div.input {
display:none;
}
</style>
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
%matplotlib inline
import seaborn as sns
import plotly.express as px
from plotly.subplots import make_subplots
sns.set(context="notebook", style="whitegrid", palette="deep", font="Arial", font_scale=1, color_codes=True)
from sklearn.linear_model import LinearRegression
from datetime import timedelta
import plotly.graph_objects as go
import scipy.optimize as opt
from statsmodels.api import OLS
from IPython.display import display, HTML
from scipy.optimize import curve_fit
#Read in data. Canada data is from the Canada gov website itself
data_src="https://github.com/CSSEGISandData/COVID-19/raw/master/csse_covid_19_data/csse_covid_19_time_series/"
cfm=pd.read_csv(data_src+"time_series_covid19_confirmed_global.csv")
dt=pd.read_csv(data_src+"time_series_covid19_deaths_global.csv")
can=pd.read_csv("https://health-infobase.canada.ca/src/data/covidLive/covid19.csv")
can=can[can['prname']!='Canada'][['date','prname','numtotal','numdeaths']]
can.date=pd.to_datetime(can.date,format='%d-%m-%Y').dt.strftime('%Y-%m-%d')
can['Country']='Canada'
tmp_cfm=can.pivot_table(values='numtotal',index=['Country','prname'],columns='date')
tmp_dt=can.pivot_table(values='numdeaths',index=['Country','prname'],columns='date')
def df_reshape(df,df_can,var, reglist):
df.rename(columns={'Country/Region':'Country', 'Province/State':'Region'},inplace=True)
df.Region.fillna('',inplace=True)
df=df.drop(columns=['Lat', 'Long']).groupby(['Country','Region']).sum()
df.columns=pd.to_datetime(pd.Series(df.columns),format='%m/%d/%y').dt.strftime('%Y-%m-%d')
maxdate=min(max(df.columns),max(df_can.columns))
df=df[df.columns[df.columns<=maxdate]]
df_can=df_can[df_can.columns[df_can.columns<=maxdate]]
tp=pd.MultiIndex.from_tuples(tuple([['Canada',x] for x in reglist]))
new_can=pd.DataFrame(index=tp,columns=df.columns)
for pr in reglist:
new_can.loc[('Canada',pr),df_can.columns]=df_can.loc[('Canada',pr)]
new_can.fillna(0,inplace=True)
new_can=new_can.cummax(axis=1)
df.drop(index='Canada',inplace=True)
df=df.append(new_can)
idxname=df.index.names
df_new=pd.melt(df.reset_index(),id_vars=idxname,\
value_vars=df.columns,var_name='Date_updated',value_name=var)
df_new[var+'_added']=df_new[var]-df_new.groupby(idxname)[var].shift(1).fillna(0)
df_new['Date_updated']=pd.to_datetime(df_new['Date_updated'])
return(df_new, df)
def gauss_two(x, mu1, sig1, a1, mu2, sig2, a2):
y1=a1*np.exp(-np.power(x - mu1, 2.) / (2 * np.power(sig1, 2.)))
y2=a2*np.exp(-np.power(x - mu2, 2.) / (2 * np.power(sig2, 2.)))
return(y1+y2)
def gauss_one(x, mu, sig, a):
y=a*np.exp(-np.power(x - mu, 2.) / (2 * np.power(sig, 2.)))
return(y)
df, df_wide=df_reshape(cfm,tmp_cfm, var='Confirmed', reglist=can.prname.unique())
df2, df2_wide=df_reshape(dt,tmp_dt, var='Death', reglist=can.prname.unique())
df=df.merge(df2,on=['Country','Region','Date_updated'],how='left')
maxdate=df['Date_updated'].max()
date_max=(maxdate + np.timedelta64(15,'D')).strftime('%Y-%m-%d')
print(f'Data version: {maxdate.strftime("%Y-%m-%d")}')
df_country=df.groupby(['Country','Date_updated'])[['Confirmed','Death','Confirmed_added','Death_added']].sum().reset_index()
df_now=df_country.groupby('Country')[['Confirmed','Death']].max().reset_index()
dfa=df_country[df_country.Country.isin(df_now[df_now.Confirmed > 300]['Country'])].copy()
dfa.loc[:,'StartDate']=-999
for j in dfa.Country.unique():
sdate=dfa.loc[(dfa.Country==j) & (dfa.Confirmed > 100),'Date_updated'].min()
##print(f"Country: {j} case number: {dfa[(dfa.Country==j) & (dfa.Confirmed > 100)]['Confirmed'].min()} start date {sdate}")
tmp=dfa.loc[dfa.Country==j,'Date_updated']-sdate
dfa.loc[dfa.Country==j,'StartDate']=tmp.dt.days + 1
if j == 'China':
dfa.loc[dfa.Country==j,'StartDate']+=5
When aligned by the first day since the 100th case in each country, the total confirmed case number of all the countries grow similarly except for a few: it follows a steep exponential growth at the early stage which is resulted from an unhampered epidemic spread, gradually slows down after various countermeasures are taken by the governments and eventually goes to flat when the situation is under control.
Countries that are successful in the early prevention and control, like Japan and Singapore, show a much shallower growth with time; South Korea experienced a rapid growth at the early stage but managed to flatten out the curve very quick by an aggressive 'trace, test and treat' strategy.
Canada currently is still in the early rising stage with a growth rate similar to many European countries. The countermeasures taken by the governments are yet to significantly slow down the growth.
Three northern European countries, Sweden, Norway and Denmark, show much shallower growth compared to other countries and this has led to a lot of discussions on the cultural difference of the northern countries that might help to enforce the social distance more effectively.
Two European countries, Czechia and Austria, have been requiring their risidents to wear face masks in public, which is different from other country's policy. It is interesting to monitoring their case growth to see the impact of this difference.
#countrylist=allcountrylist[:10]
#countrylist.append('Japan')
cl=px.colors.qualitative.Dark24
cblue=cl[0]
corange=cl[8]
cgreen=cl[2]
cred=cl[3]
cl2=[x for x in cl if x not in [cblue,corange,cgreen,cred]]
#countrylist.remove('China').remove('Korea, South').remove('Italy')
fig=px.line(log_y=True,range_x=[0,200],range_y=[100,10000000],width=1000, height=600)
#fig=px.line(dfa[dfa.Country.isin(countrylist)],x='StartDate',y='Confirmed',color='Country',log_y=True
# ,range_x=[0,40],range_y=[100,100000])
cty='China'
clr=cblue #blue
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='Italy'
clr=corange #Orange
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2, color=clr))
cty='US'
clr=cgreen #green
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2,color=clr))
ctryl=['Spain','Germany','Iran','France','Switzerland','United Kingdom','Japan',
'Singapore','Czechia','Denmark','Sweden','Austria','Norway','Brazil','Russia']
for j in range(len(ctryl)):
fig.add_scatter(x=dfa.loc[dfa.Country==ctryl[j],'StartDate'],y=dfa.loc[dfa.Country==ctryl[j],'Confirmed']
,mode="lines",name=ctryl[j],line=dict(width=2,color=cl2[j]))
cty='Korea, South'
clr='yellow'
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines",name=cty,line=dict(width=2,color=clr))
cty='Canada'
clr=cred #red
fig.add_scatter(x=dfa.loc[dfa.Country==cty,'StartDate'],y=dfa.loc[dfa.Country==cty,'Confirmed']
,mode="lines+markers",name=cty,marker=dict(size=2,color=clr,line=dict(width=4,color=clr)))
fig.update_layout(
title="COVID-19 Cases by Country",title_x=0.5,
xaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='outside', mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
yaxis=dict(showline=True,showgrid=True,showticklabels=True,linecolor='black',linewidth=2,
ticks='inside',mirror=True,
gridcolor="rgba(0,0,0,0.2)",
tickfont=dict(
family='Arial',
size=12,
color='black',
)),
plot_bgcolor='white',
xaxis_title="Number of Days since 100th Case",
yaxis_title="Confirmed Case",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/trend_by_country"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
s1_cnd=dfa.loc[(dfa.Country=='Canada') & (dfa.Confirmed > 1000),'Date_updated'].min()
pred=pd.DataFrame({'Date_updated':[ s1_cnd + timedelta(days=x-9) for x in range(250)]})
pred=pred.merge(dfa.loc[dfa.Country=='Canada',['Date_updated','Confirmed']], how='left',on='Date_updated')
dcan=dfa.loc[dfa.Country=='Canada',['Date_updated','Confirmed']].tail(5)
x=np.arange(10)
xx=np.array([np.repeat(1,10),x]).transpose()
mod=OLS(np.log(dcan['Confirmed']), xx[:5]).fit()
pdx=mod.get_prediction(xx)
predmean=np.exp(pdx.predicted_mean)
conf=np.exp(pdx.conf_int(alpha=0.05))
out=pd.DataFrame({'Date_updated': dcan['Date_updated'].append(dcan['Date_updated']+timedelta(days=5)),
'predict':predmean,
'cf_low':conf[:,0],'cf_high':conf[:,1]})
pred=pred.merge(out,how='left',on='Date_updated')
pred['Daily Adds']=float('nan')
tmp=pred.Confirmed.iloc[1:(len(pred)-1)].values-pred.Confirmed.iloc[0:(len(pred)-2)].values
pred.iloc[1:(len(pred)-1),5]=tmp.copy()
cty='US'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='Brazil'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='Russia'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
cty='United Kingdom'
tmp=dfa.loc[dfa.Country==cty,['Date_updated','Confirmed']]
tmp['Date_updted_'+cty]=tmp['Date_updated']
tmp['Date_updated']=tmp['Date_updated'] - tmp.loc[tmp.Confirmed > 1000, 'Date_updated'].min() + s1_cnd
tmp.rename(columns={"Confirmed": cty + " - Date Adjusted"}, inplace=True)
pred=pred.merge(tmp,how='left', on='Date_updated')
eve=[['2020-03-13','Recommendation against Intl. travel'],
['2020-03-16','State of Emergency'],
['2020-03-17','Restricted entry into Canada'],
['2020-03-20','US-Canada border closed for non-essential travel'],
['2020-03-24','Quarantine Act invoked'],
['2020-03-30','Domestic travel restriction']]
eve=pd.DataFrame(eve, columns=['Date_updated','events'])
eve.Date_updated=pd.to_datetime(eve.Date_updated)
pred=pred.merge(eve,how='left',on='Date_updated')
pred['predict_adds']=pred['predict']-pred['predict'].shift(1)
#pred.to_csv('output/CanadaGrowth'+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+'.csv')
Since Canada is still at the exponential growth period, an exponential projection provides a good short-term forecast. In the following chart, the next 5 day's case number is projected based on the exponential growth of the previous 5 day's actual case numbers. The different countermeasures taken by the government are timelined on the chart to monitor their impacts on the growth.
After reaching 1000 cases, the growth rate of Canada is actually not very different from the growth rate of many other countries when they had a similar number of cases. The plot below shows the growth curve of Canada vs US, Italy, Korean and China after other countries' dates are backshifted. The trajectories of those countries provide different scenaria on the long term case growth in Canada.
As of today, we can see that the Canada growth curve finally started to fall below both US and Italy, suggesting we are slowing down faster than those countries.
fig=px.line(log_y=True,range_x=['2020-03-08',date_max],range_y=[100,10000000],width=900, height=600)
captext="Canada COVID-19 case growth with time. US, Brazil, Russia and UK's curves are shifted by days to align with<br>"\
+ "the 1000th case day in Canada. Exponential projection of the future 5 days are calculated using the previous 5 day<br>"\
+"numbers. The shaded area indicates the 95% confidence interval of the projection."
cl=['green','orange','gold','blue']
cty=['US', 'Brazil', 'Russia', 'United Kingdom']
lname=['US - date shifted', 'Brazil - date shifted',
'Russia - date shifted','United Kingdom - date shifted']
for i in range(len(cl)):
fig.add_scatter(x=pred['Date_updated'],y=pred[cty[i] +' - Date Adjusted'], mode="lines",
name=lname[i],line=dict(width=3,color=cl[i]))
fig.add_trace(go.Scatter(x=pred['Date_updated'], y=pred['cf_low'],
fill=None, mode='lines',line_color='rgba(0,0,0,0)',showlegend=False))
fig.add_trace(go.Scatter(x=pred['Date_updated'], y=pred['cf_high'],
fill='tonexty',mode='lines', line_color='rgba(256,0,0,0.3)',showlegend=False))
fig.add_scatter(x=pred['Date_updated'],y=pred['predict'], mode="lines",
name='Canada exponential projection',line=dict(width=2, color='red', dash='dot'))
fig.add_scatter(x=pred['Date_updated'],y=pred['Confirmed'], mode="lines+markers+text",
name='Canada',marker=dict(size=5,color='red'),
text=pred['events'],textposition="bottom right",
textfont=dict(family="Ariel",size=14,color='red'))
fig.update_layout(title='Canada COVID-19 Case Growth Curve',title_x=0.5,
titlefont=dict(family='Arial',size=28,color='black'),
plot_bgcolor='white',
xaxis=dict(titlefont=dict(family='Ariel, sans-serif',size=11, color='black'),
showline=True,showgrid=True,showticklabels=True,linecolor='black',
linewidth=2,ticks='outside', mirror=False,
gridcolor="rgba(0,0,0,0.1)",
tickfont=dict(family='Arial',size=15,color='black'),
tickformat= "%d-%b"
),
yaxis=dict(title='Confirmed Case', titlefont=dict(family='Ariel',size=18, color='black'),
showline=True,showgrid=True,showticklabels=True,linecolor='black',
linewidth=2,ticks='outside', mirror=False,
gridcolor="rgba(0,0,0,0.1)",
tickfont=dict(family='Arial',size=15,color='black')
),
legend=dict(x=0.63, y=0.05,
font=dict(family="Ariel",size=16,color="black"), bgcolor='rgba(0,0,0,0)'),
margin = dict(l = 50, r = 50, t = 60, b = 120),
annotations = [dict(x = -0.08, y = -0.25, align='left',
text = captext, showarrow = False, xref='paper', yref='paper',
xanchor='left', yanchor='auto', xshift=0, yshift=0,
font=dict(family='Arial',size=16, color="grey"))]
)
fig.show()
#fig.write_image("output/Canada_growth_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
outtable=pred.loc[~pred.predict.isna(),['Date_updated','Confirmed','predict','cf_low','cf_high','Daily Adds', 'predict_adds']].tail(8).copy()
outtable['Date']=outtable['Date_updated'].dt.strftime('%B %d')
outtable['Actual']=[ f"{x:,.0f}" if ~np.isnan(x) else '' for x in outtable['Confirmed'] ]
outtable['Projection']=[f"{x:,.0f}" for x in outtable['predict']]
outtable['Daily Adds']=[f"{x:,.0f}" if ~np.isnan(x) else '' for x in outtable['Daily Adds'] ]
outtable['Projected Adds']=[f"{x:,.0f}" for x in outtable['predict_adds']]
outtable=outtable[['Date','Actual','Projection','Daily Adds','Projected Adds']].set_index('Date').T
display(HTML(outtable.to_html()))
fig = make_subplots(
rows=5, cols=1,
subplot_titles=("Canada","US", "Brazil","Russia","UK"),
vertical_spacing=0.08,
specs=[[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}]])
bw=1000*3600*24
oneday=86400000.0
dd=10
#date_max='2020-08-30'
#Canada
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Canada','Date_updated'],
y=dfa.loc[dfa.Country=='Canada','Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5
),
row=1, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Canada','Date_updated'],
y=dfa.loc[dfa.Country=='Canada','Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5
),
row=1, col=1, secondary_y=True)
fig.add_trace(go.Bar(x=pd.to_datetime(['2020-02-26','2020-03-13','2020-03-17','2020-03-25'])
, y=np.repeat(100000,4),name='events', marker_color='rgb(256,0,0)', width=bw*0.2,showlegend=False),
row=1,col=1, secondary_y=False)
fig.add_annotation(x=pd.to_datetime('2020-02-26'), y=2700, ax=140, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Minister of Health issued warning for COVID-19", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-13'), y=2500, ax=100, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Recommendation against Intl. travel", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-17'),y=2200, ax=110, ay=0, xref='x1',yref='y1',arrowhead=1,
align='left',text="Travel bans; State of Emergency by Provs", font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-25'), y=2000, ax=60, ay=0,xref='x1',yref='y1',arrowhead=1,
align='left',text="Quarantine Act", font=dict( size=11))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=1, col=1,showgrid=True)
yr=2800
fig.update_yaxes(title_text='Case',range=[0,yr], row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=1, col=1, secondary_y=True,color='blue')
#US
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='US','Date_updated'],
y=dfa.loc[dfa.Country=='US','Confirmed_added'] ,
name='US',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=2, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='US','Date_updated'],
y=dfa.loc[dfa.Country=='US','Death_added'] ,
name='US',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=2, col=1, secondary_y=True)
fig.add_trace(go.Bar(x=pd.to_datetime(['2020-02-29','2020-03-19'])
, y=np.repeat(100000,2),name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False),
row=2, col=1, secondary_y=False)
fig.add_annotation(x=pd.to_datetime('2020-02-29'),y=55000, ax=80, ay=0, xref='x2', yref='y3',arrowhead=1,
text="WA: State of Emergency",font=dict( size=11))
fig.add_annotation(x=pd.to_datetime('2020-03-19'), y=60000, ax=100, ay=0, xref='x2', yref='y3',arrowhead=1,
text="More states lockdown; Travel bans",font=dict( size=11))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=2, col=1,showgrid=True)
yr=80000
fig.update_yaxes(title_text='Case',range=[0,yr], row=2, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=2, col=1, secondary_y=True,color='blue')
#Brazil
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Brazil','Date_updated'],
y=dfa.loc[dfa.Country=='Brazil','Confirmed_added'] ,
name='Brazil',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=3, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Brazil','Date_updated'],
y=dfa.loc[dfa.Country=='Brazil','Death_added'] ,
name='Brazil',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=3, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=pd.to_datetime(['2020-03-09'])
# , y=np.repeat(100000,1),name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False ),
# row=3, col=1, secondary_y=False)
#fig.add_annotation(x=pd.to_datetime('2020-03-10'),y=7000, ax=100,ay=0,xref='x3', yref='y5',arrowhead=1,
# text="Lockdown: Mar 09",font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-22'),y=6500,ax=70,ay=0, xref='x3', yref='y5',arrowhead=1,
# text="Peak: Mar 21",font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-05-15'),y=6000,ax=0,ay=0, xref='x3', yref='y5',
# text="From lockdown to peak:<br>12 days",font=dict(color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=3, col=1,showgrid=True)
yr=100000
fig.update_yaxes(title_text='Case',range=[0,yr], row=3, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=3, col=1, secondary_y=True,color='blue')
#Russia
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Russia','Date_updated'],
y=dfa.loc[dfa.Country=='Russia','Confirmed_added'] ,
name='Russia',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='Russia','Date_updated'],
y=dfa.loc[dfa.Country=='Russia','Death_added'] ,
name='Russia',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=[pd.to_datetime('2020-02-25')]
# , y=[100000], name='events', marker_color='rgb(256,0,0)',width=bw*0.2,showlegend=False),
# row=4, col=1, secondary_y=False)
#fig.add_annotation(
# x=pd.to_datetime('2020-02-26'),y=1000,ax=120,ay=0,xref='x4',yref='y7',arrowhead=1,
# text="Soft lockdown <br> aggressive testing and tracing: <br> Feb 25", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-04'),y=700,ax=70, ay=0, xref='x4',yref='y7',arrowhead=1,
# text="Peak: Mar 3", font=dict(size=11))
#fig.add_annotation(
# x=pd.to_datetime('2020-05-03'),y=800,ax=0,ay=0,xref='x4',yref='y7',
# text="From aggressive measures to peak: <br> 7 days",font=dict( color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=4, col=1,showgrid=True)
yr=12000
fig.update_yaxes(title_text='Case',range=[0,yr], row=4, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=4, col=1, secondary_y=True,color='blue')
#United Kingdom
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='United Kingdom','Date_updated'],
y=dfa.loc[dfa.Country=='United Kingdom','Confirmed_added'] ,
name='United Kingdom',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=5, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=dfa.loc[dfa.Country=='United Kingdom','Date_updated'],
y=dfa.loc[dfa.Country=='United Kingdom','Death_added'] ,
name='United Kingdom',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=5, col=1, secondary_y=True)
#fig.add_trace(go.Bar(x=pd.to_datetime(['2020-01-23','2020-03-25'])
# , y=np.repeat(100000,2),name='events', marker_color='rgb(256,0,0)',width=bw*0.5,showlegend=False),
# row=5,col=1, secondary_y=False)
#fig.add_annotation(
# x=pd.to_datetime('2020-01-23'),y=16000,ax=80,ay=0,xref='x5',yref='y9',arrowhead=1,
# text="Hubei lockdown:<br> Jan 23", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-03-25'),y=12000,ax=-70, ay=0, xref='x5',yref='y9',arrowhead=1,
# text="Lockdown Lifted: <br> Mar 25", font=dict(size=11))
#fig.add_annotation(x=pd.to_datetime('2020-02-13'),y=10000,ax=70, ay=0, xref='x5',yref='y9',arrowhead=1,
# text="Peak: Feb 11", font=dict(size=11))
#fig.add_annotation(
# x=pd.to_datetime('2020-05-15'),y=14000,ax=0,ay=0,xref='x5',yref='y9',
# text="Lockdown to peak: <br> 21 days",font=dict( color="black",size=14))
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=10*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-02-23',date_max]), row=5, col=1,showgrid=True)
yr=6000
fig.update_yaxes(title_text='Case',range=[0,yr], row=5, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/4.], row=5, col=1, secondary_y=True,color='blue')
fig.update_layout(height=2500, width=800, showlegend=True, title_text="Daily New Adds",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/daily_newadd_death_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
dcan=df[df.Country=='Canada'].copy()
dcan.loc[:,'StartDate']=-999
for j in dcan.Region.unique():
sdate=dcan.loc[(dcan.Region==j) & (dcan.Confirmed > 100),'Date_updated'].min()
##print(f"Country: {j} case number: {dfa[(dfa.Country==j) & (dfa.Confirmed > 100)]['Confirmed'].min()} start date {sdate}")
tmp=dcan.loc[dcan.Region==j,'Date_updated']-sdate
dcan.loc[dcan.Region==j,'StartDate']=tmp.dt.days + 1
#date_max='2020-07-10'
fig = make_subplots(
rows=4, cols=1,
subplot_titles=("Ontario", "Quebec","Alberta","British Columbia"),
vertical_spacing=0.08,
specs=[[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}],
[{"secondary_y": True}]])
bw=1000*3600*24
oneday=86400000.0
dd=10
##Ontario
ixd=(df.Country=='Canada') & (df.Region =='Ontario')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5,showlegend=False
),
row=1, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5,showlegend=False
),
row=1, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=1, col=1,showgrid=True)
yr=800
fig.update_yaxes(title_text='Case',range=[0,yr], row=1, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=1, col=1, secondary_y=True,color='blue')
#Quebec
ixd=(df.Country=='Canada') & (df.Region =='Quebec')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=2, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=2, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=2, col=1,showgrid=True)
yr=2250
fig.update_yaxes(title_text='Case',range=[0,yr], row=2, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/2.], row=2, col=1, secondary_y=True,color='blue')
#Alberta
ixd=(df.Country=='Canada') & (df.Region =='Alberta')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=3, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=3, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=3, col=1,showgrid=True)
yr=500
fig.update_yaxes(title_text='Case',range=[0,yr], row=3, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=3, col=1, secondary_y=True,color='blue')
#British Columbia
ixd=(df.Country=='Canada') & (df.Region =='British Columbia')
fig.add_trace(go.Bar(x=df.loc[ixd ,'Date_updated'],
y=df.loc[ixd,'Confirmed_added'] ,
name='Case',
marker_color='rgb(55, 83, 109)',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=False)
fig.add_trace(go.Bar(x=df.loc[ixd,'Date_updated'],
y=df.loc[ixd,'Death_added'] ,
name='Death',
marker_color='blue',
width=bw*0.5 ,showlegend=False
),
row=4, col=1, secondary_y=True)
fig.update_xaxes(title_text="Date", showline=True,linewidth=1,showticklabels=True,
linecolor='black',ticklen=10,tickwidth=2,tickcolor='black',dtick=dd*oneday,tickformat= "%d-%b",
tickangle=30,
ticks='outside',range=pd.to_datetime(['2020-03-10',date_max]), row=4, col=1,showgrid=True)
yr=300
fig.update_yaxes(title_text='Case',range=[0,yr], row=4, col=1, secondary_y=False)
fig.update_yaxes(title_text='Death',range=[0,yr/5.], row=4, col=1, secondary_y=True,color='blue')
fig.update_layout(height=2000, width=800, showlegend=True, title_text="Daily New Adds",
font=dict(
family="Arial",
size=14,
color="black"
)
)
fig.show()
#fig.write_image("output/daily_newadd_death_Canada_"+ dfa.Date_updated.max().strftime('_%m_%d_%Y')+".jpeg", scale=3)
Three scenarios from CIDRAP 2020
xlong=list(range(0,1000))
xdate_long=[pd.to_datetime('2020-01-22')+np.timedelta64(x,'D') for x in xlong]
sce=pd.DataFrame({'x': xlong, 'date': xdate_long, \
'case': float('NaN'), 'case_city': float('NaN'),\
'case_sm': float('NaN'), 'case_city_sm': float('NaN')}).set_index('date')
dft_dl=pd.read_excel("https://docs.google.com/uc?export=download&id=1euhrML0rkV_hHF1thiA0G5vSSeZCqxHY",\
sheet_name="Cases by Reported Date").set_index('Reported Date')
sce.loc[dft_dl.index,'case_city']=dft_dl['Case Count']
sce.loc[(sce.index <= dft_dl.index.max()) & (sce.case_city.isna()),'case_city']=0
sce.loc[dfa.Date_updated[dfa.Country=='Canada'],'case']=dfa.loc[dfa.Country=='Canada','Confirmed_added'].values
sce.loc[(sce.index < dfa.Date_updated[dfa.Country=='Canada'].max()) & (sce.case.isna()),'case']=0
sce['case_city_sm']=sce['case_city'].rolling(5,min_periods=1).mean()
sce['case_sm']=sce['case'].rolling(5,min_periods=1).mean()
sce.loc[sce['case'].isna(),'case_sm']=float("NaN")
sce.loc[sce['case_city'].isna(),'case_city_sm']=float("NaN")
#two gaussian fit for Canada
popt=[96.60058132, 22.35279574, 1639.36962114, 187.78573285, 53.77234086, 415.11806907]
sce['Canada_fit']=gauss_two(sce.x,*popt)
tsep=150
#Peak and Valley
sc=sce['Canada_fit']
for i in range(1,6):
p=[popt[0]+ tsep*i+35, popt[1], popt[2]*0.95**i, popt[3] + tsep*i, popt[4], popt[5]*0.95**i]
sc=sc+gauss_two(sce.x,*p)
sce['sc1 Canada']=sc
sc=sce['Canada_fit']
sc=sc+gauss_one(sce.x, popt[0]+183, popt[1], popt[2]*2.) \
+ gauss_one(sce.x, popt[3]+183, popt[4], popt[5]*0.95)
for i in range(2,5):
p=[popt[0]+ tsep*i, popt[1], popt[2]*0.9**i, popt[3] + tsep*i, popt[4], popt[5]*0.95**i]
sc=sc+gauss_two(sce.x,*p)
sce['sc2 Canada']=sc
sc=sce['Canada_fit']
for i in range(1,10):
p=[popt[3] + tsep/2*i , popt[4]*0.5, popt[5]*0.98**i]
sc=sc+gauss_one(sce.x,*p)
sce['sc3 Canada']=sc
#flu season peak 2021-Jan-26
flu_peak=[370, 33, 700]
sce['Flu']=gauss_one(sce.x,*flu_peak)
sce.reset_index(inplace=True)
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc1 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 1: Peaks and Valleys",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))
fig.show()
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc2 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 2: Fall Peak",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))
fig.show()
fig = make_subplots(rows=1, cols=1, specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=sce.date,y=sce['sc3 Canada'], fill='tozeroy'
,mode="none",fillcolor='rgba(173,216,230,0.7)', name='Scenario'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_sm,mode='lines', name='Case - Canada',
line_color='rgb(128, 128, 128)'))
fig.add_trace(go.Scatter(x=sce.date,y=sce.case_city_sm,mode='lines', name='Case - Toronto' ,
line_color='rgb(0,128,0)'), secondary_y=True)
fig.add_trace(go.Scatter(x=sce.date,y=sce.Flu, mode='lines',name='Regular Flu Season',
line = dict(color='royalblue', width=1, dash='dash')))
fig.update_yaxes(tickvals=[1000,2000,3000,4000,5000], title_text='Canada Daily Adds',range=[0,5000])
fig.update_yaxes(tickvals=np.array([1000,2000,3000,4000,5000])/8, title_text='Toronto Daily Adds',range=[0,5000/8.],
secondary_y=True,color='rgb(0,128,0)')
fig.update_xaxes(title_text='Date',range=['2020-02-01','2022-01-30'])
fig.update_layout(height=500, width=800,
title_text="Scenario 3: Slow Burn",
title_x=0.9,
title_y=0.9,
font=dict(family="Arial, sans-serif",size=16,color="black"),
margin=dict(l=20, r=20, t=20, b=20),
legend=dict(bgcolor='rgba(0,0,0,0)',
yanchor="top",
y=0.8,
xanchor="left",
x=0.6))